/**
 * @file fish.c
 * @brief Main file for the fish plugin, contains core plugin implementation.
 * @author Klemen Strnisa <strnisa.klemen@gmail.com>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include <libxfce4panel/xfce-panel-plugin.h>
#include <libxfce4util/libxfce4util.h>
#include <libxfce4ui/libxfce4ui.h>
#include "fish.h"


#define UPDATE_TIMEOUT 300                              /**< Update period for the fish panel animation (in milliseconds). */
#define NUMBER_OF_FRAMES 8                              /**< Number of frames in the fish panel animation. */
#define FISH_FULL "xfce4-fish-plugin/pixmaps/fish-transparent.png"  /**< Path to the source image containing fish panel animation frames (relative to plugin resource folder). */
#define FISH_IMAGE_HEIGHT 22                            /**< Fish source image height (in pixels). */
#define FISH_IMAGE_WIDTH 36                             /**< Fish source image width (in pixels). */
#define FISH_IMAGE_BORDER 2                             /**< Fish image border (in pixels). */
#define FISH_TEXT_MARGIN 10                             /**< Fortune dialog window margin (in pixels). */
#define FISH_BUTTON_HEIGHT 40                           /**< Fortune dialog button height (in pixels). */
#define FISH_BUTTON_WIDTH 100                           /**< Fortune dialog button width (in pixels). */
#define FISH_WINDOW_HEIGHT 400                          /**< Fortune dialog window height (in pixels). */
#define FISH_WINDOW_WIDTH 640                           /**< Fortune dialog window width (in pixels). */
#define FISH_WINDOW_BORDER 10                           /**< Fortune dialog window border (in pixels). */
#define FISH_BOX_SPACING 5                              /**< Fortune dialog inter-element spacing (in pixels). */
#define FISH_DEFAULT_COMMAND "fortune"                  /**< Default fish command. */
#define FISH_DEFAULT_NAME "Wanda"                       /**< Default fish name. */

static void fish_construct(XfcePanelPlugin *plugin);
static void fish_write_config(XfcePanelPlugin *plugin, FishPlugin *fish);
static void fish_read_config(XfcePanelPlugin *plugin, FishPlugin *fish);

XFCE_PANEL_PLUGIN_REGISTER(fish_construct);


/*****************************
 *** Fish Plugin Functions ***
 *****************************/


/**
 * @brief Draw the next image in the fish panel animation.
 * @param [in,out] fish Private context for the fish plugin.
 *
 * This function executes periodically on FishPlugin#timer, with the period defined
 * by #UPDATE_TIMEOUT. It takes the next image in the animation and scales it
 * to appropriate size depending on the panel orientation.
 */
static void fish_draw(FishPlugin *fish) {
    GdkPixbuf   *frame;
    int         width = FISH_IMAGE_WIDTH;
    int         height = FISH_IMAGE_HEIGHT;

    /* Update current frame counter. */
    fish->current_frame = (fish->current_frame + 1) % NUMBER_OF_FRAMES;

    /* Scale image to panel size, depending on orientation. */
    if(xfce_panel_plugin_get_orientation(fish->plugin) ==
            GTK_ORIENTATION_HORIZONTAL) {
        height = fish->size - FISH_IMAGE_BORDER;
        width = width * height/FISH_IMAGE_HEIGHT;
    } else {
        width = fish->size - FISH_IMAGE_BORDER;
        height = height * width/FISH_IMAGE_WIDTH;
    }
    /* This gives a new instance of a pixbuf so we have to free it at the end. */
    frame = gdk_pixbuf_scale_simple(fish->frames[fish->current_frame],
            width, height, GDK_INTERP_BILINEAR);

    /* Set new image from the scaled pixbuf and free it. */
    gtk_image_set_from_pixbuf(GTK_IMAGE(fish->image), frame);
    g_object_unref(G_OBJECT(frame));
}


/**
 * @brief Fill the frames array and create fish panel animation image.
 * @param [in,out] fish Private context for the fish plugin.
 *
 * This function is executed once when the plugin is loaded. It retrieves the
 * fish panel animation frames from plugin resource storage, splits it into
 * separate pixbufs and creates the animation image.
 */
static void fish_load_image(FishPlugin *fish) {
    gchar       *fishfull_filename = NULL;
    GdkPixbuf   *fishfull_pixbuf;
    gint        i;

    /* Retrieve fish panel animation frames from plugin resource storage. */
    fishfull_filename = xfce_resource_lookup(XFCE_RESOURCE_DATA, FISH_FULL);
    fishfull_pixbuf = gdk_pixbuf_new_from_file(fishfull_filename, NULL);

    /* Split full pixbuf into separate frames and fill frames. */
    fish->current_frame = 0;
    fish->frames = (GdkPixbuf **)malloc(NUMBER_OF_FRAMES*sizeof(GdkPixbuf *));
    for(i = 0; i < NUMBER_OF_FRAMES; i++) {
        fish->frames[i] = gdk_pixbuf_new_subpixbuf(fishfull_pixbuf,
                i*FISH_IMAGE_WIDTH, 0, FISH_IMAGE_WIDTH, FISH_IMAGE_HEIGHT);
    }
    g_free(fishfull_filename);
    g_object_unref(G_OBJECT(fishfull_pixbuf));

    /* Create fish panel animation image. */
    fish->image = gtk_image_new_from_pixbuf(fish->frames[fish->current_frame]);
}


/**
 * @brief Hide fortune dialog window (on delete-event).
 * @param [in,out] window FishPlugin#fortune_window
 * @param [in] event @c delete-event for FishPlugin#fortune_window
 * @param [in] fish Private context for the fish plugin.
 *
 * @retval TRUE Always returns true.
 *
 * The window is only hidden so that the it keeps the placement and size after
 * it is closed and opened again.
 */
static gboolean destroy_fortune_window(GtkWidget *window, GdkEvent *event, FishPlugin *fish) {
    gtk_widget_hide(window);
    return TRUE;
}


/**
 * @brief Update fortune dialog window with new text.
 * @param [in,out] fish Private context for the fish plugin.
 *
 * Does the following:
 *  - call FishSettings#command
 *  - update FishPlugin#fortune_buffer with the response
 */
static void update_fortune_window(FishPlugin *fish) {
    gboolean    result;
    gchar       *std_out, *std_err, *output;

    result = g_spawn_command_line_sync(fish->settings.command,
            &std_out, &std_err, NULL, NULL);

    if (result) {
        output = g_strdup_printf("\n%s", std_out);
        gtk_text_buffer_set_text(fish->fortune_buffer, output, -1);
        g_free(output);
    }

    g_free(std_out);
    g_free(std_err);
}


/**
 * @brief Display fortune dialog window.
 * @param [in,out] fish Private context for the fish plugin.
 *
 * Does the following:
 *  - if FishPlugin#fortune_window doesn't exist yet, create it:
 *  	- label
 *  	- text entry
 *  	- next and close buttons
 *  - call #update_fortune_window to update FishPlugin#fortune_buffer with new text
 *  - show FishPlugin#fortune_window
 */
static void display_fortune_window(FishPlugin *fish) {
    if (!fish->fortune_window) {

        GtkWidget *view, *scrolled, *hbox, *vbox, *label, *next, *close;

        /* top level window */
        fish->fortune_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        g_signal_connect(fish->fortune_window, "delete-event",
                G_CALLBACK(destroy_fortune_window), fish);
        gtk_container_set_border_width(GTK_CONTAINER(fish->fortune_window),
                FISH_WINDOW_BORDER);
        gtk_window_set_default_size(GTK_WINDOW(fish->fortune_window),
                FISH_WINDOW_WIDTH, FISH_WINDOW_HEIGHT);

        /* top level vertical box */
        vbox = gtk_vbox_new(FALSE, FISH_BOX_SPACING);
        gtk_container_add(GTK_CONTAINER(fish->fortune_window), vbox);

        /* label for the top of the window */
        fish->fortune_label = gtk_label_new("");
        gtk_box_pack_start(GTK_BOX(vbox), fish->fortune_label, FALSE, FALSE, 0);

        /* text view fot the fortune */
        view = gtk_text_view_new();
        gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
        gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
        gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), FISH_TEXT_MARGIN);
        gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), FISH_TEXT_MARGIN);
        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
        fish->fortune_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
        gtk_text_buffer_create_tag(GTK_TEXT_BUFFER(fish->fortune_buffer),
                "monospace_tag", "family", "Monospace", NULL);

        /* scrolled window for the text view if text is too long */
        scrolled = gtk_scrolled_window_new(NULL, NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
                GTK_SHADOW_IN);
        gtk_container_add(GTK_CONTAINER(scrolled), view);
        gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);

        /* next and close buttons */
        hbox = gtk_hbox_new (FALSE, FISH_BOX_SPACING);
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
        next = gtk_button_new_with_label("Speak Again");
        gtk_widget_set_size_request(next, FISH_BUTTON_WIDTH, FISH_BUTTON_HEIGHT);
        close = gtk_button_new_with_label("Close");
        gtk_widget_set_size_request(close, FISH_BUTTON_WIDTH, FISH_BUTTON_HEIGHT);
        gtk_box_pack_end(GTK_BOX(hbox), close, FALSE, FALSE, 0);
        gtk_box_pack_end(GTK_BOX(hbox), next, FALSE, FALSE, 0);
        g_signal_connect_swapped(close, "clicked",
                G_CALLBACK(gtk_widget_hide), fish->fortune_window);
        g_signal_connect_swapped(next, "clicked",
                G_CALLBACK(update_fortune_window), fish);
    }

    update_fortune_window(fish);

    gtk_window_set_title(GTK_WINDOW(fish->fortune_window), fish->title_text);
    gtk_label_set_markup(GTK_LABEL(fish->fortune_label), fish->label_text);

    gtk_widget_show_all(fish->fortune_window);
}


/**
 * @brief handle button event (on @c clicked).
 * @param [in] plugin FishPlugin#plugin.
 * @param [in] event @c clicked for the buttons in FishPlugin#fortune_window
 * @param [in,out] fish Private context for the fish plugin.
 *
 * @retval TRUE Left mouse-button was clicked so window was closed.
 * @retval FALSE Did nothing.
 */
static gboolean fish_handle_button(XfcePanelPlugin *plugin, GdkEvent *event, FishPlugin *fish) {
    guint button = ((GdkEventButton*)event)->button;
    if (button == 1) {
        display_fortune_window(fish);
        return TRUE;
    }

    return FALSE;
}



/*************************
 *** Properties Dialog ***
 *************************/


/**
 * @brief Update fortune dialog window with new fish name.
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_update_name(FishPlugin *fish) {
    g_free(fish->title_text);
    fish->title_text =
            g_strdup_printf("%s the Fish", fish->settings.name);

    g_free(fish->label_text);
    fish->label_text =
            g_strdup_printf("<big><big>%s the Fish says:</big></big>",
                    fish->settings.name);
}


/**
 * @brief Save fish settings from settings dialog widgets to #FishSettings struct.
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_write_settings(FishPlugin *fish) {
    const gchar *temp;

    g_free(fish->settings.name);
    temp = gtk_entry_get_text(GTK_ENTRY(fish->settings.name_setting));
    fish->settings.name = g_strdup(temp);

    g_free(fish->settings.command);
    temp = gtk_entry_get_text(GTK_ENTRY(fish->settings.command_setting));
    fish->settings.command = g_strdup(temp);

    fish_update_name(fish);
}


/**
 * @brief Read fish settings from #FishSettings struct to settings dialog widgets.
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_read_settings(FishPlugin *fish) {
    gtk_entry_set_text(GTK_ENTRY(fish->settings.name_setting),
            fish->settings.name);

    gtk_entry_set_text(GTK_ENTRY(fish->settings.command_setting),
            fish->settings.command);
}


/**
 * @brief Set default fish settings into the #FishSettings struct.
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_default_settings(FishPlugin *fish) {
    if(!(fish->settings.name))
        fish->settings.name = g_strdup(FISH_DEFAULT_NAME);

    if(!(fish->settings.command))
        fish->settings.command = g_strdup(FISH_DEFAULT_COMMAND);
}


/**
 * @brief Response to the closing of the properties dialog.
 * @param [in,out] dialog Fish plugin properties dialog.
 * @param [in] response Unused.
 * @param [in,out] fish Private context for the fish plugin.
 *
 * Calls #fish_write_settings and #fish_write_config. Then destroys the
 * properties dialog window.
 */
static void fish_properties_dialog_response(GtkWidget *dialog, gint response, FishPlugin *fish) {
    fish_write_settings(fish);
    fish_write_config(fish->plugin, fish);

    xfce_panel_plugin_unblock_menu(fish->plugin);
    gtk_widget_destroy(dialog);
}


/**
 * @brief Display the fish properties dialog window.
 * @param [in,out] plugin FishPlugin#plugin
 * @param [in,out] fish Private context for the fish plugin.
 *
 * Does the following:
 *  - create the properties dialog:
 *  	- Pages:
 *  		- Fortune sources
 *  		- Display
 *  - call #fish_read_settings
 *  - show the properties dialog
 */
static void fish_properties_dialog(XfcePanelPlugin *plugin, FishPlugin *fish) {
    GtkWidget   *dialog, *content_area, *hbox, *vbox, *label, *notebook;

    xfce_panel_plugin_block_menu(plugin);

    dialog = xfce_titled_dialog_new_with_buttons("Fish",
            GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(plugin))),
            GTK_DIALOG_DESTROY_WITH_PARENT,
            GTK_STOCK_CLOSE, GTK_RESPONSE_OK, NULL);

    xfce_titled_dialog_set_subtitle(XFCE_TITLED_DIALOG(dialog), "Properties");
    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
    gtk_window_set_icon_name(GTK_WINDOW(dialog), "xfce4-fish-plugin");

    g_signal_connect(dialog, "response",
            G_CALLBACK(fish_properties_dialog_response), fish);

    /* Top notebook for the settings dialog. */
    notebook = gtk_notebook_new();
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(notebook), FISH_BOX_SPACING);


    /* "Fortune sources" notebook page. */
    vbox = gtk_vbox_new(FALSE, FISH_BOX_SPACING);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), FISH_BOX_SPACING);
    label = gtk_label_new_with_mnemonic("Fortune sources");
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);

    /* Fortune command. */
    hbox = gtk_hbox_new(FALSE, FISH_BOX_SPACING);
    label = gtk_label_new("Fortune command:");
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    fish->settings.command_setting = gtk_entry_new();
    gtk_box_pack_start(GTK_BOX(hbox), fish->settings.command_setting, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);


    /* "Display" notebook page. */
    vbox = gtk_vbox_new(FALSE, FISH_BOX_SPACING);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), FISH_BOX_SPACING);
    label = gtk_label_new_with_mnemonic("Display");
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);

    /* Fish name. */
    hbox = gtk_hbox_new(FALSE, FISH_BOX_SPACING);
    label = gtk_label_new("Fish name:");
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    fish->settings.name_setting = gtk_entry_new();
    gtk_box_pack_start(GTK_BOX(hbox), fish->settings.name_setting, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

    /* Set settings from struct members to widgets. */
    fish_read_settings(fish);

    gtk_widget_show_all(dialog);
}



/*************************
 *** About Dialog ***
 *************************/


/**
 * @brief Response to the closing of the about dialog.
 * @param [in,out] dialog Fish plugin about dialog.
 * @param [in] response Unused.
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_about_dialog_response(GtkWidget *dialog, gint response, FishPlugin *fish) {
    xfce_panel_plugin_unblock_menu(fish->plugin);
    gtk_widget_destroy(dialog);
}


/**
 * @brief Display the fish about dialog window.
 * @param [in,out] plugin FishPlugin#plugin
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_about_dialog(XfcePanelPlugin *plugin, FishPlugin *fish) {
    GtkWidget   *dialog, *hbox, *label;

    xfce_panel_plugin_block_menu(plugin);

    dialog = xfce_titled_dialog_new_with_buttons("Fish the Fish",
        GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(plugin))),
        GTK_DIALOG_DESTROY_WITH_PARENT,
        GTK_STOCK_CLOSE, GTK_RESPONSE_OK, NULL);

    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
    gtk_window_set_icon_name(GTK_WINDOW(dialog), "xfce4-fish-plugin");

    g_signal_connect(dialog, "response",
            G_CALLBACK(fish_about_dialog_response), fish);

    hbox = gtk_hbox_new(FALSE, FISH_BOX_SPACING);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(hbox), FISH_WINDOW_BORDER);

    label = gtk_label_new("Shamelessly copied from the GNOME fish applet.\n\
        No rights reserved whatsoever.\n\n\
        Klemen Strnisa\t2012");
    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);

    gtk_widget_show_all(dialog);
}



/******************************
 *** Panel Plugin Functions ***
 ******************************/


/**
 * @brief Free all plugin resources.
 * @param [in,out] plugin FishPlugin#plugin
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_free_data(XfcePanelPlugin *plugin, FishPlugin *fish) {
    int i;

    g_return_if_fail(plugin != NULL);
    g_return_if_fail(fish != NULL);

    for (i = 0; i < NUMBER_OF_FRAMES; i++) {
        g_object_unref(G_OBJECT(fish->frames[i]));
    }

    gtk_widget_destroy(fish->ebox);
    if (fish->fortune_window) {
        gtk_widget_destroy(fish->fortune_window);
    }

    g_free(fish->title_text);
    g_free(fish->label_text);

    g_free(fish->settings.command);
    g_free(fish->settings.name);

    if (fish->settings.command_setting) {
        gtk_widget_destroy(fish->settings.command_setting);
    }
    if (fish->settings.name_setting) {
        gtk_widget_destroy(fish->settings.name_setting);
    }

    g_source_remove(fish->timer);
    g_free(fish);
}


/**
 * @brief Calculate effective size for the fish panel animation image.
 * @param [in,out] plugin FishPlugin#plugin
 * @param [in,out] size FishPlugin#size
 * @param [in,out] fish Private context for the fish plugin.
 *
 * @retval TRUE Always returns @c TRUE
 */
static gboolean fish_set_size(XfcePanelPlugin *plugin, int size, FishPlugin *fish) {
    if (xfce_panel_plugin_get_orientation (plugin) == GTK_ORIENTATION_HORIZONTAL) {
        gtk_widget_set_size_request(GTK_WIDGET(plugin), -1, size);
    } else {
        gtk_widget_set_size_request(GTK_WIDGET(plugin), size, -1);
    }

    fish->size = size;
    fish_draw(fish);

    return TRUE;
}


/**
 * @brief Read fish plugin settings from permanent storage plugin resources.
 * @param [in,out] plugin FishPlugin#plugin
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_read_config(XfcePanelPlugin *plugin, FishPlugin *fish) {
    XfceRc      *rc;
    char        *file;
    const char  *value;

    if (!(file = xfce_panel_plugin_lookup_rc_file(plugin))) {
        return;
    }

    rc = xfce_rc_simple_open(file, TRUE);
    g_free(file);

    if (!rc) {
        return;
    }

    if ((value = xfce_rc_read_entry(rc, "name", NULL)) && *value) {
        fish->settings.name = g_strdup(value);
    }

    if ((value = xfce_rc_read_entry(rc, "command", NULL)) && *value) {
        fish->settings.command = g_strdup(value);
    }

    xfce_rc_close(rc);
}


/**
 * @brief Write fish plugin settings to permanent storage plugin resources.
 * @param [in,out] plugin FishPlugin#plugin
 * @param [in,out] fish Private context for the fish plugin.
 */
static void fish_write_config(XfcePanelPlugin *plugin, FishPlugin *fish) {
    XfceRc  *rc;
    gchar   *file;

    if (!(file = xfce_panel_plugin_save_location(plugin, TRUE))) {
        return;
    }

    rc = xfce_rc_simple_open(file, FALSE);
    g_free(file);

    if (!rc) {
        return;
    }

    xfce_rc_write_entry(rc, "name", fish->settings.name);
    xfce_rc_write_entry(rc, "command", fish->settings.command);

    xfce_rc_close(rc);
}


/**
 * @brief Create fish private context and start panel animation.
 * @param [out] plugin FishPlugin#plugin
 *
 * Called at plugin load. Does the following
 *  - Creates and allocates private context for the plugin
 *  - creates a GtkEventBox for clicks on the fish animation
 *  - calls #fish_load_image to create the animation
 *  - creates FishPlugin#timer which starts the animation
 *
 *  @retval fish Private context for the fish plugin
 */
static FishPlugin *fish_create(XfcePanelPlugin *plugin) {
    FishPlugin  *fish = g_new0(FishPlugin, 1);

    fish->plugin = plugin;
    fish->ebox = gtk_event_box_new();

    fish_load_image(fish);

    gtk_widget_add_events(fish->ebox, GDK_BUTTON_PRESS_MASK);
    gtk_container_add(GTK_CONTAINER(fish->ebox), fish->image);

    gtk_widget_show_all(GTK_WIDGET(fish->ebox));

    fish->timer = g_timeout_add(UPDATE_TIMEOUT, (GSourceFunc)fish_draw, fish);

    return fish;
}


/**
 * @brief Construct the plugin.
 * @param [in,out] plugin FishPlugin#plugin
 *
 * Called at plugin load. Does the following
 *  - calls #fish_create
 *  - connects appropriate events for the plugin
 */
static void fish_construct(XfcePanelPlugin *plugin) {
    FishPlugin *fish;

    xfce_textdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");

    fish = fish_create(plugin);

    fish_read_config(plugin, fish);
    fish_default_settings(fish);
    fish_update_name(fish);

    g_signal_connect(plugin, "size-changed",
            G_CALLBACK(fish_set_size), fish);

    g_signal_connect(plugin, "free-data",
            G_CALLBACK(fish_free_data), fish);

    g_signal_connect(plugin, "save",
            G_CALLBACK(fish_write_config), fish);

    g_signal_connect(fish->ebox, "button-press-event",
            G_CALLBACK(fish_handle_button), fish);

    xfce_panel_plugin_menu_show_configure(plugin);
    g_signal_connect (plugin, "configure-plugin",
            G_CALLBACK (fish_properties_dialog), fish);

    xfce_panel_plugin_menu_show_about(plugin);
    g_signal_connect (plugin, "about",
            G_CALLBACK (fish_about_dialog), fish);

    gtk_container_add(GTK_CONTAINER(plugin), fish->ebox);
    xfce_panel_plugin_add_action_widget(plugin, fish->ebox);
}
